library ieee;
use ieee.std_logic_1164.all;
entity sync_fifo is
	generic (depth: integer := 4;
				data_width:integer := 32);
	port ( data_in: in std_logic_vector(data_width-1 downto 0);
			 data_out: out std_logic_vector(data_width -1 downto 0);
			 empty: out std_logic;
			 full: out std_logic;
			 internal_cnt: out integer range 0 to depth;
			 wrreq: in std_logic;
			 rdreq: in std_logic;
			 clk: in std_logic;
			 rst: in std_logic
	);	
end sync_fifo;
architecture arch_sync_fifo of sync_fifo is
signal counter :integer range 0 to depth :=0;
type mem_t is array(depth -1 downto 0) of std_logic_vector(data_width - 1 downto 0);
signal ram : mem_t;
signal enable:std_logic_vector(1 downto 0);
procedure increase_or_clear(obj:inout integer;
									 threshold:in integer) is
begin 
	if(obj=threshold) then
		obj := 0;
	else
		obj := obj +1;
	end if;
end increase_or_clear;
begin
	enable <= (rdreq,wrreq);
	process(rst,clk)
	variable rd_pointer,wr_pointer : integer range 0 to depth-1 :=0;
	begin
		if(rst='1') then
			counter <= 0;
			rd_pointer := 0;
			wr_pointer := 0;
			data_out <= (others => '0');
		elsif(clk'event and clk = '1') then
			case enable is
				when "01" =>
					counter <= counter +1;
					ram(wr_pointer) <= data_in;
					increase_or_clear(wr_pointer,depth-1);
				when "10" =>
					counter <= counter -1;
					data_out <= ram(rd_pointer);
					increase_or_clear(rd_pointer,depth-1);
				when "11" =>
					if(counter = 0) then
						data_out <= data_in;
					else
						ram(wr_pointer) <= data_in;
						data_out <= ram(rd_pointer);
						increase_or_clear(wr_pointer,depth-1);
						increase_or_clear(rd_pointer,depth-1);
					end if;
				when others =>
			end case;
		end if;
	end process;
	empty <= '1' when counter = 0 else '0';
	full <= '1' when counter = depth else '0';
	internal_cnt <= counter;
end arch_sync_fifo;
